/*
 *    Copyright (c) 2006 ThoughtWorks, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.thoughtworks.turtlemock.internal;

import com.thoughtworks.turtlemock.constraint.Constraint;
import junit.framework.Assert;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class InvokeResultLogs {
    private class InvokeMethodResultLogWrapper implements InvokeMethodResultLog {
        private final InvokeMethodResultLog log;

        protected InvokeMethodResultLogWrapper(InvokeMethodResultLog log) {
            this.log = log;
        }

        public void rollback() {
            log.rollback();
        }

        public void times(int expectedTimes) {
            log.times(expectedTimes);
        }

        public void twice() {
            log.twice();
        }

        public int timesNotAsserted() {
            return log.timesNotAsserted();
        }

        public InvokeWithArgsResultLog with(Object param) {
            return log.with(param);
        }

        public InvokeWithArgsResultLog with(Object param1, Object param2) {
            return log.with(param1, param2);
        }

        public InvokeWithArgsResultLog with(Object param1, Object param2, Object param3) {
            return log.with(param1, param2, param3);
        }

        public InvokeWithArgsResultLog with(Object param1, Object param2, Object param3, Object param4) {
            return log.with(param1, param2, param3, param4);
        }

        public InvokeWithArgsResultLog withArgs(Object[] args) {
            return log.withArgs(args);
        }

        public String getOperationName() {
            return log.getOperationName();
        }

        public InvokeWithArgsResultLog withAllArgsConstraint(Constraint c) {
            return log.withAllArgsConstraint(c);
        }

        public InvokeMethodResultLog inAnotherThread() {
            return log.inAnotherThread();
        }

        public InvokeWithArgsResultLog withFirstArgConstraint(Constraint constraint) {
            return log.withFirstArgConstraint(constraint);
        }

        public InvokeWithArgsResultLog withArgsConstraint(Constraint[] constrains) {
            return log.withArgsConstraint(constrains);
        }

        public InvokeWithArgsResultLog withArgConstraint(Constraint constraint, int paramIndex) {
            return log.withArgConstraint(constraint, paramIndex);
        }

        public InvokeWithArgsResultLog noArgs() {
            return log.noArgs();
        }

        public InvokeMethodResultLog then(String operationName) {
            int index = list.indexOf(this);

            String msg = "expected did operation \"" + log.getOperationName() + "\" and then did operation \"" + operationName + "\", ";
            Assert.assertTrue(msg + "but no operation after.", (++index) < list.size());

            InvokeMethodResultLog nextLog = (InvokeMethodResultLog) list.get(index);
            Assert.assertTrue(msg + "but operation \"" + nextLog.getOperationName() + "\" after.", nextLog.getOperationName().equals(operationName));
            nextLog.times(1);
            return nextLog;
        }

        public String toString() {
            return "InvokeMethodResultLogWrapper:" + getOperationName();
        }

    }

    private Map map = new HashMap();

    private List list = new ArrayList();

    public void add(InvokeMethodResultLog newTop) {
        InvokeMethodResultLog top = (InvokeMethodResultLog) map.get(newTop.getOperationName());
        if (top != null) {
            putIn(new PairInvokeMethodResultLog(top, newTop));
        } else {
            putIn(newTop);
        }
    }

    private void putIn(InvokeMethodResultLog log) {
        InvokeMethodResultLogWrapper wrapper = new InvokeMethodResultLogWrapper(log);
        map.put(wrapper.getOperationName(), wrapper);
        list.add(wrapper);
    }

    public InvokeMethodResultLog getByOperationName(String name) {
        Assert.assertNotNull("operation name should not be null!", name);
        return (InvokeMethodResultLog) map.get(name);
    }

    public boolean isEmpty() {
        return map.isEmpty() && list.isEmpty();
    }

}
